import { Callout } from "nextra-theme-docs";

# Docker + K8s 分离部署

本文中以轻量化部署的 K3s 为例，若您已经部署了 K8s 集群，可以参考本文后续配置进行类似的设置。

<Callout type="warning">

本篇教程请务必全部阅读后再进行操作，否则可能导致部署结果达不到预期。

</Callout>

## 安装 Docker

你可以在 [Docker 官网](https://docs.docker.com/engine/install/) 找到相关的安装说明，并使用 [快速上手](/quick-start) 中的基本配置将 GZCTF 运行起来。

## 安装 k3s

k3s 是一个轻量级的 k8s 发行版，可以在单机和多机上快速部署 k8s 集群。官网文档地址：[https://docs.k3s.io/zh](https://docs.k3s.io/zh)

<Callout type="info">

如果你的机器只有一台，并想通过 Docker 运行 GZCTF 可以在安装时指定添加以下参数指定 Docker 后端（不推荐）：

```bash
INSTALL_K3S_EXEC="--docker"
```

<Callout type="info">

如果需要在一台 k3s 实例上运行超过 255 个题目容器，**需要在安装 k3s 时** 指定 `INSTALL_K3S_EXEC`，并更改 `node-cidr-mask-size` 为所需的子网大小。

```bash
INSTALL_K3S_EXEC="--kube-controller-manager-arg=node-cidr-mask-size=16"
```

**上述配置项在安装后无法更改。** CIDR 修改为 `/16` 可以支持 65535 个 Pod。

上述配置仅修改了节点 Pod 使用的 IP 地址范围，如果需要修改节点的 Pod 数量限制，请参考下文。

</Callout>

并使用如下格式安装 k3s，如需更多信息请参考 [k3s 安装配置](https://docs.k3s.io/installation/configuration)：

```bash
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="..." sh -
```

但这种方法是不推荐的，使用 Docker 作为容器后端可能会导致一些功能不可用。

如果不想使用 Docker 作为容器后端，但是需要单机部署，那么可以参考 [K8s 集群部署](/deployment/k8s-only) 中的方法。

</Callout>

```bash
curl -sfL https://get.k3s.io | sh -
# Check for Ready node, takes ~30 seconds
sudo k3s kubectl get node
```

<Callout type="info">

中国用户，可以使用以下方法加速安装：

```bash
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh -
```

</Callout>

多机安装和集群组建请参考 [官方文档](https://docs.k3s.io/zh/quick-start)。

## 配置 GZCTF

k3s 的连接配置文件位于 `/etc/rancher/k3s/k3s.yaml`，可以使用以下命令导出：

```bash
sudo cat /etc/rancher/k3s/k3s.yaml
```

使用如下命令获取 k3s control-panel 所在机器的 IP：

```bash
sudo k3s kubectl cluster-info
```

<Callout type="info">

若显示 `127.0.0.1` 则说明 k3s control-panel 就是当前的机器，请使用 `ip a` 查看当前机器的 IP。

你可以直接使用 IP 地址，或者使用域名，但是需要确保域名解析到 k3s control-panel 所在的机器，确保 GZCTF 的机器可以访问它的 6443 端口。

</Callout>

将上述输出的内容保存为 `kube-config.yaml`，并更改 `server` 字段为 k3s control-panel 所在机器的 IP，例如

```yaml
apiVersion: v1
clusters:
  - cluster:
      certificate-authority-data: # ...
      server: https://127.0.0.1:6443 # change this to your k3s control-panel's IP or domain
    name: default
# ...
```

将其存储至部署 GZCTF 的机器上，和 `compose.yml` 在一个文件夹内，例如 `kube-config.yaml`。
之后更改 `compose.yml` 中的挂载信息：

```yaml
gzctf:
  image: registry.cn-shanghai.aliyuncs.com/gztime/gzctf:latest
  restart: always
  ports:
    - "80:8080"
  networks:
    default:
  volumes:
    - "./data/files:/app/files"
    - "./appsettings.json:/app/appsettings.json:ro"
    - "./kube-config.yaml:/app/kube-config.yaml:ro" # this is required for k8s deployment
    # - "/var/run/docker.sock:/var/run/docker.sock" # this is required for docker deployment
  depends_on:
    - db
```

同时更改 `appsettings.json`，设置 `ContainerProvider` 字段：

```json
{
  "Type": "Kubernetes",
  "PublicEntry": "ctf.example.com" // change this to your k3s control-panel's IP or domain
}
```

重新启动 GZCTF，之后就可以使用 k3s 作为容器后端了。已经使用过 k8s 的用户也可以参考上述的配置过程，让 GZCTF 接入现有的 k8s 集群。

## 更改 NodePort 端口范围

k3s 默认的 NodePort 端口范围为 30000-32767，这可能会与需求不太符合，因此可以根据需要更改 k3s 的 NodePort 端口范围。

在 k3s control-panel 所在的机器上执行以下命令：

- `sudo nano /etc/systemd/system/k3s.service`
- 编辑如下设置中的 `ExecStart`，指定`service-node-port-range`

  ```bash
  ExecStart=/usr/local/bin/k3s \
      server \
      --kube-apiserver-arg=service-node-port-range=20000-50000
  ```

- `sudo systemctl daemon-reload`
- `sudo systemctl restart k3s`

## 更改 K3s 的容器数量限制

k3s 默认的容器数量限制为 110，这可能不适用于比赛中的大量小容器，因此可以根据需要更改 k3s 的容器数量限制。

<Callout type="info">

Reference: [How do you increase maximum pods per node in K3S?](https://stackoverflow.com/questions/65894616/how-do-you-increase-maximum-pods-per-node-in-k3s)

</Callout>

在 k3s control-panel 所在的机器上执行以下命令：

- `sudo nano /etc/rancher/k3s/kubelet.config`
- 编辑如下设置中的 `maxPods`

  ```yaml
  apiVersion: kubelet.config.k8s.io/v1beta1
  kind: KubeletConfiguration
  maxPods: 500
  ```

- `sudo nano /etc/systemd/system/k3s.service`

- 编辑如下设置中的 `ExecStart`，指定`kubelet-arg`

  ```bash
  ExecStart=/usr/local/bin/k3s \
      server \
      --kubelet-arg=config=/etc/rancher/k3s/kubelet.config
  ```

- `sudo systemctl daemon-reload`
- `sudo systemctl restart k3s`

## 添加容器镜像仓库

直接使用外部的容器镜像仓库无法直接在 k3s 中使用，需要在 k3s 中添加镜像仓库。

在 k3s control-panel 所在的机器上执行以下命令：

- `sudo nano /etc/rancher/k3s/registries.yaml`

- 编辑如下设置中的 `mirrors`，指定你所需要的镜像仓库地址

  ```yaml
  mirrors:
    "container.ctf.example.com": # change this to your registry's domain
      endpoint:
        - "https://container.ctf.example.com" # change this to your registry's domain
  ```

- `sudo systemctl restart k3s`
